/*
 * @(#)StubFactoryImpl.java	1.9 03/12/03
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.presentation.rmi ;

import java.security.AccessController;
import java.security.PrivilegedAction;

import java.lang.reflect.Method ;
import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.Proxy ;
import java.lang.reflect.InvocationTargetException ;

import java.io.ObjectInputStream ;
import java.io.ObjectOutputStream ;
import java.io.IOException ;

import java.rmi.Remote ;

import javax.rmi.CORBA.Util ;

import org.omg.CORBA.portable.ObjectImpl ;
import org.omg.CORBA.portable.Delegate ;
import org.omg.CORBA.portable.ServantObject ;
import org.omg.CORBA.portable.ApplicationException ;
import org.omg.CORBA.portable.RemarshalException ;

import org.omg.CORBA.SystemException ;

import com.sun.corba.se.spi.orb.ORB ;

import com.sun.corba.se.pept.transport.ContactInfoList ;

import com.sun.corba.se.spi.transport.CorbaContactInfoList ;

import com.sun.corba.se.spi.protocol.CorbaClientDelegate ;
import com.sun.corba.se.spi.protocol.LocalClientRequestDispatcher ;

import com.sun.corba.se.spi.presentation.rmi.IDLNameTranslator ;
import com.sun.corba.se.spi.presentation.rmi.DynamicMethodMarshaller ;
import com.sun.corba.se.spi.presentation.rmi.PresentationManager ;
import com.sun.corba.se.spi.presentation.rmi.StubAdapter ;

import com.sun.corba.se.spi.orbutil.proxy.InvocationHandlerFactory ;
import com.sun.corba.se.spi.orbutil.proxy.LinkedInvocationHandler ;

import com.sun.corba.se.impl.corba.CORBAObjectImpl ;

public final class StubInvocationHandlerImpl implements LinkedInvocationHandler  
{
    private transient PresentationManager.ClassData classData ;
    private transient PresentationManager pm ;
    private transient org.omg.CORBA.Object stub ;
    private transient Proxy self ;

    public void setProxy( Proxy self )
    {
	this.self = self ;
    }

    public Proxy getProxy()
    {
	return self ;
    }

    public StubInvocationHandlerImpl( PresentationManager pm,
	PresentationManager.ClassData classData, org.omg.CORBA.Object stub ) 
    {
	SecurityManager s = System.getSecurityManager();
	if (s != null) {
	    s.checkPermission(new DynamicAccessPermission("access"));
	}
	this.classData = classData ;
	this.pm = pm ;
	this.stub = stub ;
    }

    private boolean isLocal()
    {
	boolean result = false ;
	Delegate delegate = StubAdapter.getDelegate( stub ) ;

	if (delegate instanceof CorbaClientDelegate) {
	    CorbaClientDelegate cdel = (CorbaClientDelegate)delegate ;
	    ContactInfoList cil = cdel.getContactInfoList() ;
	    if (cil instanceof CorbaContactInfoList) {
		CorbaContactInfoList ccil = (CorbaContactInfoList)cil ;
		LocalClientRequestDispatcher lcrd = 
		    ccil.getLocalClientRequestDispatcher() ;
		result = lcrd.useLocalInvocation( null ) ;
	    }
	}
	 
	return result ;
    }

    /** Invoke the given method with the args and return the result.
     *  This may result in a remote invocation.
     *  @param proxy The proxy used for this class (null if not using java.lang.reflect.Proxy)
     */
    public Object invoke( Object proxy, final Method method, 
	Object[] args ) throws Throwable
    {
	String giopMethodName = classData.getIDLNameTranslator().
	    getIDLName( method )  ;
	DynamicMethodMarshaller dmm = 
	    pm.getDynamicMethodMarshaller( method ) ;

	Delegate delegate = null ;
	try {
	    delegate = StubAdapter.getDelegate( stub ) ;
	} catch (SystemException ex) {
	    throw Util.mapSystemException(ex) ;
	} 

	if (!isLocal()) {
	    try {
		org.omg.CORBA_2_3.portable.InputStream in = null ;
		try {
		    // create request
		    org.omg.CORBA_2_3.portable.OutputStream out = 
			(org.omg.CORBA_2_3.portable.OutputStream)
			delegate.request( stub, giopMethodName, true);

		    // marshal arguments
		    dmm.writeArguments( out, args ) ;

		    // finish invocation
		    in = (org.omg.CORBA_2_3.portable.InputStream)
			delegate.invoke( stub, out);
	
		    // unmarshal result
		    return dmm.readResult( in ) ;
		} catch (ApplicationException ex) {
		    throw dmm.readException( ex ) ;
		} catch (RemarshalException ex) {
		    return invoke( proxy, method, args ) ; 
		} finally {
		    delegate.releaseReply( stub, in );
		}
	    } catch (SystemException ex) {
		throw Util.mapSystemException(ex) ;
	    } 
	} else {
	    // local branch
	    ORB orb = (ORB)delegate.orb( stub ) ;
	    ServantObject so = delegate.servant_preinvoke( stub, giopMethodName,
		method.getDeclaringClass() );
	    if (so == null) {
		return invoke( stub, method, args ) ; 
	    }
	    try {
		Object[] copies = dmm.copyArguments( args, orb ) ;

	        if (!method.isAccessible()) {	
		    // Make sure that we can invoke a method from a normally
		    // inaccessible package, as this reflective class must always
		    // be able to invoke a non-public method.
		    AccessController.doPrivileged(new PrivilegedAction() {
			public Object run() {
			    method.setAccessible( true ) ;
			    return null ;
			} 
		    } ) ;
		}

		Object result = method.invoke( so.servant, copies ) ;

		return dmm.copyResult( result, orb ) ;
	    } catch (InvocationTargetException ex) {
		Throwable mex = ex.getCause() ;
		// mex should never be null, as null cannot be thrown
		Throwable exCopy = (Throwable)Util.copyObject(mex,orb);
		if (dmm.isDeclaredException( exCopy ))
		    throw exCopy ;
		else
		    throw Util.wrapException(exCopy);
	    } catch (Throwable thr) {
		if (thr instanceof ThreadDeath)
		    throw (ThreadDeath)thr ;

		// This is not a user thrown exception from the
		// method call, so don't copy it.  This is either
		// an error or a reflective invoke exception.
		throw Util.wrapException( thr ) ;
	    } finally {
		delegate.servant_postinvoke( stub, so);
	    }
	}
    }
}
